#include "irdecoder.h"
#include "crc7.h"

#include <stdio.h>
#include <string.h>

typedef enum {
	ZERO_BIT = 0,
	ONE_BIT	 = 1,
	STOP_BIT = 2,
	START_BIT = 3
} bit_t;

#define DATA_MASK			0x0100

static int IRCarrierFrequency = 38000;	// 38KHZ
static int IRCarrierFrequencyRes = 0;
static float IRCarrierClockDuty = 0.0;
static int StartBit = 1;
static int StopBit = 1;
static int TimeResolutionUs = 1;
static int CarrierClockRes;
static int BitLowRes;
static char BitBuffer[256];
static int TotalBits = 0;
static unsigned char ByteBufferLSB[32];
static unsigned char ByteBufferMSB[32];
static int TotalBytes = 0;
static FILE *DatFh;
static int LogicalOneForestRes = 0;
static int LogicalOneFlatRes = 0;
static int LogicalZeroForestRes = 0;
static int LogicalZeroFlatRes = 0;

void decoderInit(int carrierFreq, int startBit, int stopBit, int timeResUs) {
	if(carrierFreq >= 0) {
		IRCarrierFrequency = carrierFreq;
	}

	if(startBit >= 0) {
		StartBit = startBit;
	}

	if(stopBit >= 0) {
		StopBit = stopBit;
	}

	if(timeResUs >= 0) {
		TimeResolutionUs = timeResUs;
	}

	CarrierClockRes = 1000000 / IRCarrierFrequency / TimeResolutionUs;
}

static int readOneSample() {
	unsigned short temp = 0;
	int ret;

	ret = fread(&temp, 1, 2, DatFh);
	
	if(ret != 2)
		return -1;

	return (temp & DATA_MASK) ? 1 : 0;
}

static void skipFileHeader() {
	int ret;

	// fseek(DatFh, 0x300, SEEK_SET);
	
	// skip the blank data
	while (1) {
		ret = readOneSample();
		if(ret == 0) {
			continue;
		} else {
			break;
		}
	}
}

// return resolution counts or (-1)
static int readOneCarrierClockcycle() {
	int limit = CarrierClockRes;
	int resCount[2] = {0, 0};
	int totalRes;
	int bit;
	float duty;
	do {
		bit = readOneSample();
		resCount[0] ++;
	} while (bit);
	
	// only the the LOW timeout
	do {
		bit = readOneSample();
		resCount[1] ++;
	} while ((!bit) && (-- limit));

	if(limit == 0) {
		return -1;
	} else {
		duty = ((float)resCount[0] / (float)resCount[1]);
		if (IRCarrierClockDuty > 0.0) {
			IRCarrierClockDuty = (IRCarrierClockDuty + duty) / 2;
		} else {
			IRCarrierClockDuty = duty;
		}
		
		totalRes = (resCount[0] + resCount[1]);
		if (IRCarrierFrequencyRes == 0) {
			IRCarrierFrequencyRes = totalRes;
		} else {
			IRCarrierFrequencyRes = (IRCarrierFrequencyRes + totalRes) / 2;
		}
		return totalRes;
	}
}

// return 1 - stop bit, 0 - normal data
static int readBit(int *forestRes, int *flatRes) {
	int resCount[2] = {0, 0};
	int limit = BitLowRes;
	int ret;
	
	do {
		ret = readOneCarrierClockcycle();
		if(ret > 0)
			resCount[0] += ret;
	} while (ret != -1);
	
	do {
		ret = readOneSample();
		resCount[1] ++;
	} while ((ret == 0) && (-- limit));

	if (limit == 0) {
		*forestRes = 0;
		*flatRes = 0;
		return 1;
	}

	*forestRes = resCount[0];
	*flatRes = resCount[1];
	return 0;
}

static int readStartBit() {
	int forestRes, flatRes;

	if(readBit(&forestRes, &flatRes) == 0) {
		// take the start bit low peirod as 
		// bit low peirod time limt
		printf("start bit: forest %d us, flat %d us\n", forestRes, flatRes);
		BitLowRes = flatRes;
		return 0;
	}
	return -1;
}

static bit_t readLogicalBit() {
	int forestRes, flatRes, ret;
	float rate;

	ret = readBit(&forestRes, &flatRes);
	
	if(ret == 1) {
		return STOP_BIT;
	} else {

		rate = (float)flatRes / (float)forestRes;

		if (rate > 1.5) {
			if (LogicalOneForestRes == 0) {
				LogicalOneForestRes = forestRes;
			} else {
				LogicalOneForestRes = (LogicalOneForestRes + forestRes) / 2;
			}

			if (LogicalOneFlatRes == 0) {
				LogicalOneFlatRes = flatRes;
			} else {
				LogicalOneFlatRes = (LogicalOneFlatRes + flatRes) / 2;
			}
			
			return ONE_BIT;
		} else {
			if (LogicalZeroForestRes == 0) {
				LogicalZeroForestRes = forestRes;
			} else {
				LogicalZeroForestRes = (LogicalZeroForestRes + forestRes) / 2;
			}

			if (LogicalZeroFlatRes == 0) {
				LogicalZeroFlatRes = flatRes;
			} else {
				LogicalZeroFlatRes = (LogicalZeroFlatRes + flatRes) / 2;
			}
			return ZERO_BIT;
		}
	}
}

static void showInfomation() {
	printf("carrier clock frequency: %d us, %0.2f khz\n", IRCarrierFrequencyRes, (1000.0 / (float)IRCarrierFrequencyRes)); 
	printf("carrier clock duty: %0.2f, %0.1f us high + %0.1f us low\n", IRCarrierClockDuty, 
		(float)IRCarrierFrequencyRes / (IRCarrierClockDuty + 1.0) * IRCarrierClockDuty,
		(float)IRCarrierFrequencyRes / (IRCarrierClockDuty + 1.0) * 1.0);
	printf("logical \"1\": forest %d us + flat %d us\n", LogicalOneForestRes, LogicalOneFlatRes);
	printf("logical \"0\": forest %d us + flat %d us\n\n", LogicalZeroForestRes, LogicalZeroFlatRes);
}

static void sumCheck(unsigned char *buffer, int len, unsigned char sumExp) {
	unsigned char sumGot = 0;
	int i;

	for(i = 0; i < len; i ++) {
		sumGot += buffer[i];
	}
	printf("\n");
	printf("sum check: exp 0x%x, got 0x%x, %s", sumExp, sumGot,
		(sumGot == sumExp) ? "BINGO" : "MISMATCH");
}

static void crcCheck(unsigned char *buffer, int len, unsigned char crcExp) {
	unsigned char crcGot = crc7(0, buffer, len);
	
	printf("\n");
	printf("crc check: exp 0x%x, got 0x%x, %s", crcExp, crcGot,
		(crcGot == crcExp) ? "BINGO" : "MISMATCH");
}

static void xorCheck(unsigned char *buffer, int len, unsigned char xorExp) {
	unsigned char xorGot = 0;
	int i;

	for(i = 0; i < len; i ++) {
		xorGot ^= buffer[i];
	}
	printf("\n");
	printf("xor check: exp 0x%x, got 0x%x, %s", xorExp, xorGot,
		(xorGot == xorExp) ? "BINGO" : "MISMATCH");
}

int decoderStart(FILE *datFh) {
	bit_t data;
	int stopBitMask = 0;
	int i, j;
	unsigned char buffer;
	FILE *saveFh;

	if (StartBit != 1) {
		printf("only support one start bit for now\n");
		return -1;
	}
	
	DatFh = datFh;
	/*DatFh = fopen(inputFileName, "rb");
	if(DatFh == NULL) {
		printf("file \"%s\" not found\n", inputFileName);
		return -1;
	}*/
	saveFh = fopen("record.txt", "a");
	
	skipFileHeader();
	
	// read the start bit
	if (readStartBit() != 0) {
		printf("read start bit failed\n");
		fclose(DatFh);
		return -1;
	}

	memset(BitBuffer, 0, sizeof(BitBuffer));
	TotalBits = 0;
	stopBitMask = 0;

	do {
		data = readLogicalBit();
		if(data != STOP_BIT) {
			BitBuffer[TotalBits ++] = data;
		} else {
			stopBitMask = 1;
		}
	} while( (data != STOP_BIT) && (TotalBits < sizeof(BitBuffer)) );
	
	if (stopBitMask == 0) {
		printf("no stop bit found\n");
		fclose(DatFh);
		return -1;
	}
	BitLowRes = 0;
	
	showInfomation();
	
	printf("total logical bits: %d\n", TotalBits);
	for(i = 0; i < TotalBits; i ++) {
		if((i != 0) && ((i % 4) == 0)) {
			printf(",");
		}
		if((i != 0) && ((i % 32) == 0)) {
			printf("\n");
		}
		printf("%d", BitBuffer[i]);
	}
	printf("\n\n");

	TotalBytes = 0;

	printf("LSB 4bit format:\n");
	memset(ByteBufferLSB, 0, sizeof(ByteBufferLSB));
	for(i = 0, j = 0; i < TotalBits;) {
		buffer = 0;

		if (BitBuffer[i ++]) buffer |= (1 << 0);
		if (BitBuffer[i ++]) buffer |= (1 << 1);
		if (BitBuffer[i ++]) buffer |= (1 << 2);
		if (BitBuffer[i ++]) buffer |= (1 << 3);

		printf("%x,", buffer & 0xf);

		if((i % 32) == 0) {
			printf("\n");
		}

		ByteBufferLSB[j] >>= 4;
		ByteBufferLSB[j] |= (buffer << 4);

		if((i % 8) == 0) {
			j ++;
			TotalBytes ++;
		}
	}
	printf("\n\n");
	
	printf("LSB 8bit format:\n");
	for(i = 0; i < TotalBytes; i ++) {
		printf ("%02x,", ByteBufferLSB[i]);
		fprintf(saveFh, "%02x,", ByteBufferLSB[i]);
	}
	fprintf(saveFh, "\n");
	sumCheck(ByteBufferLSB, TotalBytes - 1, ByteBufferLSB[TotalBytes - 1]);
	xorCheck(ByteBufferLSB, TotalBytes - 1, ByteBufferLSB[TotalBytes - 1]);
	crcCheck(ByteBufferLSB, TotalBytes - 1, ByteBufferLSB[TotalBytes - 1]);
	printf("\n\n");

	printf("MSB 4bit format:\n");
	memset(ByteBufferMSB, 0, sizeof(ByteBufferMSB));
	for(i = 0, j = 0; i < TotalBits;) {
		buffer = 0;

		if (BitBuffer[i ++]) buffer |= (1 << 3);
		if (BitBuffer[i ++]) buffer |= (1 << 2);
		if (BitBuffer[i ++]) buffer |= (1 << 1);
		if (BitBuffer[i ++]) buffer |= (1 << 0);

		printf("%x,", buffer & 0xf);

		if((i % 32) == 0) {
			printf("\n");
		}

		ByteBufferMSB[j] <<= 4;
		ByteBufferMSB[j] |= buffer;

		if((i % 8) == 0) {
			j ++;
		}
	}
	printf("\n\n");
	
	printf("MSB 8bit format:\n");
	for(i = 0; i < TotalBytes; i ++) {
		printf("%02x,", ByteBufferMSB[i]);
	}
	sumCheck(ByteBufferMSB, TotalBytes - 1, ByteBufferMSB[TotalBytes - 1]);
	xorCheck(ByteBufferMSB, TotalBytes - 1, ByteBufferMSB[TotalBytes - 1]);
	crcCheck(ByteBufferMSB, TotalBytes - 1, ByteBufferMSB[TotalBytes - 1]);
	printf("\n\n");

	//fclose(DatFh);
	fclose(saveFh);
	getchar();
	return 0;
}